package bma.common.langutil.core;

/**
 * 
 *  @since 1.0 (imported from BMA.ESP Project)
 *
 */
public class ULongGenerator {

	private final static long TWEPOCH = 1288834974657L;

	private final static int NODE_ID_BITES = 8;
	private final static int SEQ_BITS = 12;
	private final static int SEQ_MASK = -1 ^ (-1 << SEQ_BITS);

	private final static int NODE_ID_SHIFT = SEQ_BITS;
	private final static int TIMESTAMP_LEFT_SHIFT = SEQ_BITS + NODE_ID_BITES;

	private int nodeId = 0;
	private int sequence = 0;
	private volatile long lastTimestamp = -1l;

	public ULongGenerator() {
		super();
	}

	public ULongGenerator(int nodeId) {
		super();
		this.nodeId = nodeId;
	}

	public int getNodeId() {
		return nodeId;
	}

	public void setNodeId(int nodeId) {
		this.nodeId = nodeId;
	}

	public int getSequence() {
		return sequence;
	}

	public void setSequence(int v) {
		this.sequence = v;
	}

	public long getLastTimestamp() {
		return lastTimestamp;
	}

	public void setLastTimestamp(long lastTimestamp) {
		this.lastTimestamp = lastTimestamp;
	}

	/**
	 * 获取下一个唯一id值
	 * 
	 * @return 唯一id值
	 * 
	 */
	public synchronized long next() {
		long timestamp = System.currentTimeMillis();
		if (lastTimestamp == timestamp) {			
			sequence = (sequence + 1) & SEQ_MASK;
			if (sequence == 0) {
				timestamp = System.currentTimeMillis();
				while (timestamp <= lastTimestamp) {
					try {
						this.wait(0, 1000);
					} catch (InterruptedException e) {
						return 0;
					}
					timestamp = System.currentTimeMillis();
				}
			}
		} else {			
			sequence = 0;
		}
		if (timestamp < lastTimestamp) {
			throw new IllegalStateException("system clock inconsistent");
		}

		lastTimestamp = timestamp;
		return (timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT
				| nodeId << NODE_ID_SHIFT | sequence;
	}

}
